﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using DocumentFormat.OpenXml.CustomProperties;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.VariantTypes;
using DocumentFormat.OpenXml.Wordprocessing;
using Expect.Core;
using Expect.Core.Reports;
using Expect.Report.Word.Processors;

namespace Expect.Report.Word
{    
    class WordStyleRef
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public StyleValues StyleType { get; set; }
    }

    class ReportGenerator
    {
        private ReportConfiguration _cfg;

        private WordprocessingDocument _package;

        private Body _body;

        private Assembly _assembly;

        private ProcessorManager _manager;

        private List<WordStyleRef> _stylesCatalog;

        public ReportGenerator(ReportConfiguration cfg)
        {
            _cfg = cfg;
            _manager = new ProcessorManager(cfg);
        }

        public List<WordStyleRef> ReadTemplateStyles()
        {
            using (var package = WordprocessingDocument.Open(_cfg.TemplateFile, false))
            {
                return this.GetStyles(package);
            }
        }

        public void Execute()
        {
            _assembly = Assembly.LoadFrom(_cfg.Source);
            
            var definition = AssemblyReader.ReadFrom(_assembly, VerboseOutput);
            
            this.InitializeGeneration();
            try
            {
                GenerateDocumentProperties();
                foreach (var spec in definition.ChildSpecs)
                {
                    this.WriteSpecification(spec, 0);
                }
                _manager.LogUnknownStyles();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                this.EndGeneration();
            }
        }

        private void GenerateDocumentProperties()
        {
            Dictionary<string, string> props = new Dictionary<string, string>();
            props.Add("AssemblyVersion", _assembly.GetName().Version.ToString());
            props.Add("AssemblyName", _assembly.GetName().Name);
            props.Add("AssemblyFullName", _assembly.GetName().FullName);

            var attrs = _assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false);
            if (attrs != null && attrs.Length > 0)
            {
                string filevers = ((AssemblyFileVersionAttribute)attrs[0]).Version;
                props.Add("AssemblyFileVersion", filevers);
            }

            if (_cfg.CustomProperties != null)
            {
                foreach (var customProperty in _cfg.CustomProperties)
                {
                    props.Add(customProperty.Key, customProperty.Value);
                }
            }

            this.SetDocumentProperties(props);
        }

        private void VerboseOutput(string msg)
        {
            if (_cfg.Verbose)
                Console.WriteLine(msg);
        }

        private void InitializeGeneration()
        {
            if (!File.Exists(_cfg.TemplateFile))
            {
                Console.WriteLine("Template file not found " + _cfg.TemplateFile);
                return;
            }

            this.VerboseOutput("Copying template file from " + _cfg.TemplateFile + " to " + _cfg.Output);
            File.Copy(_cfg.TemplateFile, _cfg.Output, true);
            File.SetAttributes(_cfg.Output, FileAttributes.Normal);

            _package = WordprocessingDocument.Open(_cfg.Output, true);
            Document document = _package.MainDocumentPart.Document;
            _body = document.Body;
            _stylesCatalog = this.GetStyles(_package);
            _manager.SetStyles(_stylesCatalog);
        }

        private List<WordStyleRef> GetStyles(WordprocessingDocument package)
        {
            List<WordStyleRef> res = new List<WordStyleRef>();
            
            var styledefparts = package.MainDocumentPart.GetPartsOfType<StyleDefinitionsPart>();
            foreach (var stylesPart in styledefparts)
            {
                foreach (Style style in stylesPart.Styles.Where(s => s is Style))
                {
                    WordStyleRef elt = new WordStyleRef();
                    elt.Id = style.StyleId.Value;
                    elt.Name = style.StyleName.Val.Value;
                    elt.StyleType = style.Type.Value;
                    res.Add(elt);
                }
            }

            return res;
        }

        private void EndGeneration()
        {
            _package.Dispose();
        }

        private void WriteSpecification(SpecificationDefinition spec, int depth)
        {
            this.WriteSpecificationContent(spec, depth);

            foreach (var childSpec in spec.ChildSpecs)
            {
                this.WriteSpecification(childSpec, depth + 1);
            }
        }

        private void WriteSpecificationContent(SpecificationDefinition spec, int depth)
        {
            _package.AddParagraph(spec.Name, _manager.GetStyle(DefaultProcessor.GetStyleName(_cfg, depth)));

            foreach (var fixture in spec.Fixtures)
            {
                _manager.ProcessFixture(_assembly, _package, fixture);
                /*
                if (fixture.HasClass && (fixture.ClassType.IsSubclassOf(typeof(ImageDescriptionFixture)) || fixture.ClassType == typeof(ImageDescriptionFixture)))
                {
                    this.AddImage(fixture.Rows[0].AsText());
                }
                else if (fixture.HasClass && (fixture.ClassType.IsSubclassOf(typeof(DescriptionFixture)) || fixture.ClassType == typeof(DescriptionFixture)))
                {
                    this.GenerateTextFixture(fixture, _cfg.DescriptionsStyle);
                }
                else if (fixture.HasClass && fixture.ClassType.GetInterfaces().Any(t => t == typeof(ITableFixtureFormat)))
                {
                    GenerateTableFixture(fixture);
                }
                else
                {
                    GenerateTextFixture(fixture, _cfg.SpecificationsStyle);
                }*/

                _package.AddParagraph(string.Empty);
            }
        }

        

        private void SetDocumentProperties(Dictionary<string, string> propertiesValues)
        {
            var customprops = _package.GetPartsOfType<CustomFilePropertiesPart>();
            if (customprops != null)
            {
                foreach (var props in customprops)
                {
                    foreach (var elt in props.Properties.ChildElements)
                    {
                        if (elt is CustomDocumentProperty)
                        {
                            var prop = elt as CustomDocumentProperty;
                            
                            if (propertiesValues.ContainsKey(prop.Name) && prop.ChildElements.Count > 0 && prop.ChildElements[0] is VTLPWSTR)
                            {
                                (prop.ChildElements[0] as VTLPWSTR).Text = propertiesValues[prop.Name];
                                _package.UpdateProperty(prop.Name, propertiesValues[prop.Name]);
                            }
                        }
                    }
                }
            }
        }
    }
}
